﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using YWM.DependencyInjection;
using YWM.Entities;
using YWM.Reflection;

namespace YWM.Repositories.Extensions
{
    public static class RepositoryExtensions
    {
        public static void RegisterRepositories(this MyContainerBuilder containerBuilder, Type dbContextType, Type dbSetType)
        {
            AutoRepositoryTypesAttribute attribute = dbContextType.GetCustomAttribute<AutoRepositoryTypesAttribute>();
            if (attribute == null)
            {
                return;
            }

            foreach (EntityTypeInfo entityTypeInfo in GetEntityTypeInfos(dbContextType, dbSetType))
            {
                Type entityType = entityTypeInfo.EntityType;
                Type primaryKeyType = EntityHelper.GetPrimaryKeyType(entityType);
                Type implType;
                if (primaryKeyType == typeof(long))
                {
                    Type genericRepositoryType = attribute.RepositoryInterface.MakeGenericType(entityType);
                    implType = attribute.RepositoryImplementation.GetTypeInfo().GetGenericArguments().Length == 1
                        ? attribute.RepositoryImplementation.MakeGenericType(entityType)
                        : attribute.RepositoryImplementation.MakeGenericType(dbContextType, entityType);

                    containerBuilder.RegisterType(
                            genericRepositoryType,
                            implType,
                            DependencyLifeTime.Transient
                        );
                }

                Type genericRepositoryTypeWithPrimaryKey = attribute.RepositoryInterfaceWithPrimaryKey.MakeGenericType(entityType, primaryKeyType);
                implType = attribute.RepositoryImplementationWithPrimaryKey.GetTypeInfo().GetGenericArguments().Length == 2
                    ? attribute.RepositoryImplementationWithPrimaryKey.MakeGenericType(entityType, primaryKeyType)
                    : attribute.RepositoryImplementationWithPrimaryKey.MakeGenericType(dbContextType, entityType, primaryKeyType);

                containerBuilder.RegisterType(
                    genericRepositoryTypeWithPrimaryKey,
                    implType,
                    DependencyLifeTime.Transient
                );
            }
        }

        public static IEnumerable<EntityTypeInfo> GetEntityTypeInfos(Type dbContextType, Type dbSetType)
        {
            return
                from property in dbContextType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
                where
                    ReflectionHelper.IsAssignableToGenericType(property.PropertyType, dbSetType) &&
                    ReflectionHelper.IsAssignableToGenericType(property.PropertyType.GenericTypeArguments[0], typeof(IEntityOfTKey<>))
                select new EntityTypeInfo(property.PropertyType.GenericTypeArguments[0], property.DeclaringType);
        }
    }
}
